package ckR

import (
	"bytes"
	"db2s/arg"
	"db2s/global"
	"db2s/go-log/log"
	"db2s/outPut"
	"db2s/pubM"
	"db2s/ref"
	"errors"
	"fmt"
	"os"
	"reflect"
	"strings"
	"sync/atomic"
	"time"
)

func rowsBreakPointF(config arg.RulesS) bool {
	switch config.Sync.Breakpoint {
	case false:
		return true
	case true:
		return false
	}
	return false
}
func breakPointFuncMap() map[string]reflect.Value {
	return map[string]reflect.Value{"rows": reflect.ValueOf(rowsBreakPointF)}
}
func getBreakPointStatus(config arg.RulesS) bool {
	if funcName, ok := breakPointFuncMap()[config.CheckMode]; ok {
		args := []reflect.Value{
			reflect.ValueOf(config),
		}
		l := funcName.Call(args)
		return l[0].Interface().(bool)
	}
	return false
}

func closeAndDeleteTmpFile(plan *SchedulePlan2) {
	//	for _, v := range []*os.File{
	//		plan.tmpFileF.tmpMd5FileF,
	//		plan.tmpFileF.tmpDataFileF,
	//	} {
	//		if err := v.Close(); err != nil {
	//			global.Wlog.Error(fmt.Sprintf("close file fail.err info is %v", v))
	//			return
	//		}
	//	}
	//	for _, v := range []string{
	//		plan.tmpFileF.tmpMd5FileName,
	//		plan.tmpFileF.tmpDataFileName,
	//	} {
	//		if err := os.Remove(v); err != nil {
	//			global.Wlog.Error(fmt.Sprintf("remove file fail.err info is %v", v))
	//			return
	//		}
	//	}
}
func countXlsWriterTerminalData(plan *SchedulePlan2, logSeq int64) error {
	var (
		event      = "[countXlsWriterTerminalData]"
		vlog       string
		syncStatus string
	)
	//vlog = fmt.Sprintf("(%d) %v Start writing the execution results of table %v.%v into xls...", logSeq, event, plan.Object.Schema, plan.Object.Table)
	//debugWriteInfo(vlog)
	//if v, ok := terminalPods[getTableName(plan.Object.Schema, plan.Object.Table)]; ok {
	vlog = fmt.Sprintf("(%d) %v Start writing the execution results of table %v.%v into xls...", logSeq, event, plan.Object.Schema, plan.Object.Table)
	debugWriteInfo(vlog)
	if e := atomic.LoadInt64(plan.Status.ErrEnd); e < 0 {
		plan.TPod.Result = "error"
		syncStatus = "error"
		vlog = fmt.Sprintf("(%d) %v There is an error in table %v.%v Modify the result status information to error.", logSeq, event, plan.Object.Schema, plan.Object.Table)
		infoWriteInfo(vlog)
	} else {
		syncStatus = "finish"
	}
	plan.TPod.Seq = plan.Object.TaskSeq
	BarResultStatisticMq <- plan.TPod
	resultData := global.XCountSheetTail{Seq: fmt.Sprintf("%v", plan.Object.TaskSeq), Schema: plan.Object.Schema,
		Table: plan.Object.Table, SyncSum: plan.subTaskInfo.RecordCount, SelectRows: plan.subTaskInfo.SAccumulate, InsertRows: plan.subTaskInfo.DAccumulate,
		SyncStatus: syncStatus, TableCost: global.Second(plan.Status.SubTaskBeginTime, time.Now().UnixNano()),
		Comment: fmt.Sprintf("%v", plan.subTaskInfo.ErrorInfo.Load())}
	BarResultStatisticMq <- resultData
	vlog = fmt.Sprintf("(%d) %v The execution result of table %v.%v is sent successfully. result is {%v}", logSeq, event, plan.Object.Schema, plan.Object.Table, plan.TPod)
	debugWriteInfo(vlog)
	//} else {
	//	vlog = fmt.Sprintf("(%d) %v Table %v.%v does not exist and the execution results cannot be written to xls!!", logSeq, event, plan.Object.Schema, plan.Object.Table)
	//	infoWriteInfo(vlog)
	//}
	return nil
}
func rowsXlsWriterTerminalData(plan *SchedulePlan2, logSeq int64) error {
	var (
		event = "[rowsXlsWriterTerminalData]"
		vlog  string
	)
	vlog = fmt.Sprintf("(%d) %v Start writing the execution results of table %v.%v into xls...", logSeq, event, plan.Object.Schema, plan.Object.Table)
	debugWriteInfo(vlog)

	//if v, ok := terminalPods[getTableName(plan.Object.Schema, plan.Object.Table)]; ok {
	vlog = fmt.Sprintf("(%d) %v Start writing the execution results of table %v.%v into xls...", logSeq, event, plan.Object.Schema, plan.Object.Table)
	debugWriteInfo(vlog)
	if e := atomic.LoadInt64(plan.Status.ErrEnd); e < 0 {
		plan.TPod.Result = "error"
		vlog = fmt.Sprintf("(%d) %v There is an error in table %v.%v Modify the result status information to error.", logSeq, event, plan.Object.Schema, plan.Object.Table)
		infoWriteInfo(vlog)
	}
	plan.TPod.Seq = plan.Object.TaskSeq
	if *plan.TPod.Rows.SourceNumber != *plan.TPod.Rows.DestNumber {
		plan.TPod.Result = "bad"
	} else if *plan.TPod.Rows.InsertSum > 0 || *plan.TPod.Rows.UpdateSum > 0 || *plan.TPod.Rows.DeleteSum > 0 {
		plan.TPod.Result = "bad"
	}
	BarResultStatisticMq <- plan.TPod.Rows

	var (
		buff        bytes.Buffer
		checkStatus = "ok"
	)
	for k, v := range map[string]int64{
		"intType": *plan.statistics.IntTotal, "stringType": *plan.statistics.StringTotal,
		"missRows": *plan.statistics.MissTotal, "timeType": *plan.statistics.TimeTotal,
		"moreRows": *plan.statistics.RedundantTotal,
	} {
		if f := global.Percent(v, *plan.statistics.AbTotal); f != "0.00%" {
			buff.WriteString(fmt.Sprintf("%v (%v)", k, f))
			buff.WriteString("\n")
		}
	}
	s := buff.String()
	if strings.HasSuffix(s, "\n") {
		s = s[:len(s)-len("\n")]
	}
	buff.Reset()
	if len(s) > 0 {
		checkStatus = "abnormal"
	}
	abnormalRows := *plan.TPod.Rows.InsertSum + *plan.TPod.Rows.DeleteSum
	resultData := global.XRowsSheetTail{Seq: fmt.Sprintf("%v", plan.TPod.Seq), Schema: plan.Object.Schema,
		Table: plan.Object.Table, IndexType: plan.ReadOptimizer.IndexColumnType, SrcRows: plan.TPod.Rows.SourceNumber,
		DstRows: plan.TPod.Rows.DestNumber, AbnormalRows: &abnormalRows, InsertRows: plan.TPod.Rows.InsertSum,
		DeleteRows: plan.TPod.Rows.DeleteSum, AbnormalTypeSum: plan.statistics.AbTotal, CheckStatus: checkStatus,
		AbnormalTypeProportion: s, TableCost: global.Second(plan.Status.SubTaskBeginTime, time.Now().UnixNano()), Comment: "无"}
	BarResultStatisticMq <- resultData
	closeAndDeleteTmpFile(plan)
	//} else {
	//	vlog = fmt.Sprintf("(%d) %v Table %v.%v does not exist and the execution results cannot be written to xls!!", logSeq, event, plan.Object.Schema, plan.Object.Table)
	//	infoWriteInfo(vlog)
	//}
	return nil
}

func sampXlsWriterTerminalData(plan *SchedulePlan2, logSeq int64) error {
	//var (
	//	event = "[xlsWriterTerminalData]"
	//	vlog  string
	//)
	//vlog = fmt.Sprintf("(%d) %v Start writing the execution results of table %v.%v into xls...", logSeq, event, plan.Object.Schema, plan.Object.Table)
	//debugWriteInfo(vlog)
	//
	////if v, ok := terminalPods[getTableName(plan.Object.Schema, plan.Object.Table)]; ok {
	//vlog = fmt.Sprintf("(%d) %v Start writing the execution results of table %v.%v into xls...", logSeq, event, plan.Object.Schema, plan.Object.Table)
	//debugWriteInfo(vlog)
	//if e := atomic.LoadInt64(plan.Status.ErrEnd); e < 0 {
	//	plan.TPod.SampStat = "error"
	//	vlog = fmt.Sprintf("(%d) %v There is an error in table %v.%v Modify the result status information to error.", logSeq, event, plan.Object.Schema, plan.Object.Table)
	//	infoWriteInfo(vlog)
	//}
	//plan.TPod.Samp.Seq = fmt.Sprintf("%v", plan.Object.TaskSeq)
	//if *plan.TPod.Samp.SrcSum != *plan.TPod.Samp.DstSum {
	//	plan.TPod.Samp.SampStat = "bad"
	//} else if *plan.TPod.Samp.AbnormalSum > 0 {
	//	plan.TPod.Samp.SampStat = "bad"
	//}
	//BarResultStatisticMq <- plan.TPod.Samp
	////resultData := global.XSampsheetTatil{}
	///*
	//	SrcRows: v.Rows.SourceNumber,
	//	DstRows: v.Rows.DestNumber, AbnormalRows: &abnormalRows, InsertRows: v.Rows.InsertSum,
	//	DeleteRows: v.Rows.DeleteSum, AbnormalTypeSum: plan.abnormalDataAnalyze.abTotal, CheckStatus: checkStatus,
	//	AbnormalTypeProportion: s}
	//
	//	AbnormalInsert         *int64 `json:"修复insert数量"`
	//	AbnormalDelete         *int64 `json:"修复delete数量"`
	//	AbnormalTypeSum        *int64 `json:"差异类型总数"`
	//	AbnormalTypeProportion string `json:"数据差异分类/占比"`
	//	TableCost              string `json:"单表校验耗时(s)"`
	//	SampStat               string `json:"校验状态"`
	//*/
	//resultData := global.XSampsheetTatil{Seq: plan.TPod.Samp.Seq, Schema: plan.Object.Schema,
	//	Table: plan.Object.Table, IndexType: plan.ReadOptimizer.IndexColumnType, IndexCol: plan.ReadOptimizer.IndexColumn,
	//	SrcSum: plan.TPod.Samp.SrcSum, DstSum: plan.TPod.Samp.DstSum, SrcSamp: plan.TPod.Samp.SrcSamp, DstSamp: plan.TPod.Samp.DstSamp,
	//	AbnormalSum: plan.TPod.Samp.AbnormalSum, AbnormalInsert: plan.TPod.Samp.AbnormalInsert, AbnormalDelete: plan.TPod.Samp.AbnormalDelete,
	//	TableCost: global.Second(plan.Status.SubTaskBeginTime, time.Now().UnixNano()), SampStat: plan.TPod.Samp.SampStat, Comment: "无"}
	//BarResultStatisticMq <- resultData
	//closeAndDeleteTmpFile(plan)
	////} else {
	////	vlog = fmt.Sprintf("(%d) %v Table %v.%v does not exist and the execution results cannot be written to xls!!", logSeq, event, plan.Object.Schema, plan.Object.Table)
	////	infoWriteInfo(vlog)
	////}
	return nil
}
func getXlsWriteTerminalFunc() map[string]reflect.Value {
	return map[string]reflect.Value{
		"count": reflect.ValueOf(countXlsWriterTerminalData),
		"rows":  reflect.ValueOf(rowsXlsWriterTerminalData),
		"samp":  reflect.ValueOf(sampXlsWriterTerminalData),
	}
}
func xlsWriterTerminalData(plan *SchedulePlan2, logSeq int64) error {
	if funcName, ok := getXlsWriteTerminalFunc()[GlobalPConfigs.rules.CheckMode]; ok {
		if funcName.Kind() == reflect.Func {
			args := []reflect.Value{
				reflect.ValueOf(plan),
				reflect.ValueOf(logSeq),
			}
			s := funcName.Call(args)
			return errors.New(fmt.Sprintf("%v", s[0].Interface()))
		}
	}
	return nil
}
func writeBreakPointData(fin *os.File, s any, logSeq int64) error {
	WriteTextI := outPut.FileOut("text", map[string]any{"fileN": fin, "fileName": global.BreakPointFileName, "logSeq": logSeq,
		"bufSize": 1024 * 1024 * 4, "sqlType": "sql", "rowsS": "\n",
	})
	if !WriteTextI.AppendWrite("", s) {
		err := errors.New(fmt.Sprintf("%v", `write data fail!`))
		return err
	}
	return nil
}

func newXlsObject(baseDir string, config GlobalParametersConfig) {
	xlsFileName := fmt.Sprintf("%v/%v", baseDir, config.result.XlsV.XlsFileName)
	if !strings.Contains(xlsFileName, "/") {
		xlsFileName = fmt.Sprintf("./%v", xlsFileName)
	}
	xlsFile = outPut.NewFileOperations(outPut.Xls{
		FileName:  fmt.Sprintf("./%v.xlsx", xlsFileName),
		SheetName: []string{"terminal", "result", "miss"},
		Mode:      config.rules.CheckMode,
		Enroll:    []any{outPut.WorkSheetTerminal{}, outPut.WorkSheetResult{}, outPut.WorkSheetMiss{}},
		Safety:    config.result.XlsV.XlsSafetySwitch,
	})
}
func initTextData(baseDir string, config GlobalParametersConfig) (err error) {
	textFile = outPut.NewFileOperations(outPut.Tex{
		SheetName: []string{"terminal", "result", "miss"},
		Mode:      config.rules.CheckMode,
		Enroll:    []any{outPut.WorkSheetTerminal{Persistence: baseDir}, outPut.WorkSheetResult{Persistence: baseDir}, outPut.WorkSheetMiss{Persistence: baseDir}},
		Safety:    false,
	})
	if err = textFile.Head(); err != nil {
		errorWriteInfo(fmt.Sprintf("Failed to write the TEXT header title. error is %v", err))
		return
	}
	return
}
func initXlsData(baseDir string, config GlobalParametersConfig) (err error) {
	newXlsObject(baseDir, config)
	if err = xlsFile.Create(); err != nil {
		errorWriteInfo(fmt.Sprintf("Failed to create the XLS file. error is %v", err))
		return
	}
	if err = xlsFile.Head(); err != nil {
		errorWriteInfo(fmt.Sprintf("Failed to write the XLS header title. error is %v", err))
		return
	}
	return
}
func initResultData(baseDir string, config GlobalParametersConfig) (err error) {
	//初始化输出目录result
	if !config.result.GeneralResult {
		warnWriteInfo(fmt.Sprintf("If the xls generation parameter is set to false, this step will be skipped."))
		return
	}
	infoWriteInfo("Start initializing the Result data")
	baseDir = fmt.Sprintf("%v/xls", baseDir)
	if err = dirInit(baseDir); err != nil {
		errorWriteInfo(fmt.Sprintf("The creation of the %v directory failed. error is %v", baseDir, err))
		return
	}
	if initXlsData(baseDir, config) != nil {
		return
	}
	if initTextData(baseDir, config) != nil {
		return
	}
	infoWriteInfo(fmt.Sprintf("The result data initialization has been completed"))
	return
}
func initOtherResultFile(baseDir string, name, mode string, config GlobalParametersConfig) (result ReadWriteFilePointS, err error) {
	log.MainLog().Info(fmt.Sprintf("Start initializing the TTY BAR file"))
	breakP = pubM.NewBreakObject(pubM.BreakPoint{
		FileName: fmt.Sprintf("result/breakPoint/%v/%v/%v", name, mode, global.BreakPointFileName),
		Truncate: getBreakPointStatus(config.rules),
	})
	if breakP.Read() != nil {
		return
	}
	log.MainLog().Info(fmt.Sprintf("Start initializing the parameterRecord file"))
	if result.ParameterRecord, err = os.OpenFile(fmt.Sprintf("result/%v", global.ParameterRecord), os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0664); err != nil {
		log.ErrorLog().Error(fmt.Sprintf("init %v file fail. error info is %s", global.ParameterRecord, err))
		return
	}
	log.MainLog().Info(fmt.Sprintf("Start initializing the Terminal Output file"))
	newTextTPod = outPut.NewFileOperations(outPut.Tex{FileName: fmt.Sprintf("%v/%v.data", baseDir, global.MonitorBarFinishFileName)})
	if err = newTextTPod.Init(); err != nil {
		return
	}
	return
}
func updateModifyParameterSync(drive string, file *os.File, data []global.ModifyParameterResultS) (err error) {
	defer func() {
		if err = file.Close(); err != nil {
			return
		}
	}()
	infoWriteInfo(fmt.Sprintf("Start recording the %v write optimization parameters", drive))
	for _, v := range data {
		debugWriteInfo(fmt.Sprintf("Record one of the MySQL write optimization parameters, %v", global.AnyToJsonString(v)))
		if _, err = file.WriteString(fmt.Sprintf("%v\n", global.AnyToJsonString(v))); err != nil {
			warnWriteInfo(fmt.Sprintf("Record the failure of the MySQL write optimization parameters. parameter:%v error:%v", global.AnyToJsonString(v), err))
			return
		}
	}
	return
}
func InitGlobalParametersConfig(l *SchemaInitContextConfig) (result *GlobalParametersConfig) {
	var m = l.GtCheckConfig
	return &GlobalParametersConfig{
		repair:  m.SecondaryL.RepairV,
		rules:   m.SecondaryL.RulesV,
		dSns:    m.SecondaryL.DSnsV,
		structs: m.SecondaryL.RulesV.Struct,
		result:  m.SecondaryL.ResultV,
	}
}
func InitCloseObject(m arg.ConfigParameter, result *GlobalParametersConfig) {
	//closeObject = quit.GetClose(map[string]any{
	//	"dsn": map[string]any{"name": m.SecondaryL.DSnsV.DestDBName,
	//		"drive": m.SecondaryL.DSnsV.DestDrive,
	//		"jdbc":  m.SecondaryL.DSnsV.DestJdbc,
	//	},
	//}, p.DDB, []*os.File{result.FilePoint.BreakPointFin, result.FilePoint.ActiveDataFin, result.FilePoint.TtyBarFin, result.FilePoint.ObjectResult})
	global.GetIoc().RegisterContext("closeObject", closeObject)
}

// NewSchedulePlanConfig 初始化全局共享变量
func NewSchedulePlanConfig(l *SchemaInitContextConfig) (result *GlobalParametersConfig, err error) {
	var baseDir = fmt.Sprintf("result/end/%v_%v", l.GtCheckConfig.SecondaryL.RulesV.CheckMode, time.Now().Format("2006-01-02T15-04-05"))
	var event = "[NewSchedulePlanConfig]"
	result = InitGlobalParametersConfig(l)
	if result.db, err = pubM.InitDSnsPool(l.GtCheckConfig); err != nil {
		err = ref.ErrAddPrintf(event, err)
		log.ErrorLog().Error(fmt.Sprintf("create db connection fail. error info is {%s}", err))
		return
	}
	if _, err = pubM.InitResultData(baseDir, l.GtCheckConfig); err != nil {
		err = ref.ErrAddPrintf(event, err)
		log.ErrorLog().Error(fmt.Sprintf("init result datadir fail. error info is {%s}", err))
		return
	}
	if err = initResultData(baseDir, *result); err != nil {
		err = ref.ErrAddPrintf(event, err)
		log.ErrorLog().Error(fmt.Sprintf("init xls result datadir fail. error info is {%s}", err))
		return
	}
	if result.FilePoint, err = initOtherResultFile(baseDir, l.GtCheckConfig.ArgInput.RowsDataCmd.Name, l.GtCheckConfig.ArgInput.RowsDataCmd.Mode, *result); err != nil {
		err = ref.ErrAddPrintf(event, err)
		log.ErrorLog().Error(fmt.Sprintf("init other result datadir fail. error info is {%s}", err))
		return
	}
	if pubM.UpdateModifyParameterSync(result.db.Target, result.FilePoint.ParameterRecord) != nil {
		err = ref.ErrAddPrintf(event, err)
		log.ErrorLog().Error(fmt.Sprintf("update modify parameter fail. error info is {%s}", err))
		return
	}
	InitCloseObject(l.GtCheckConfig, result)
	global.GetIoc().RegisterContext("globalConfig", result)
	return
}

func initSchemaBean(config *SchemaInitContextConfig) (err error) {
	var event = "[initSchemaBean]"
	//注册输入配置
	global.GetIoc().RegisterContext("configParameter", config.GtCheckConfig)
	defer func() {
		if err != nil {
			log.ErrorLog().Error(fmt.Sprintf("%v init plan Config fail. error is {%v}", event, err))
		}
	}()
	//初始化全局共享配置 	//注册执行计划配置 SchedulePlanConfig
	if GlobalPConfigs, err = NewSchedulePlanConfig(config); err != nil || GlobalPConfigs == nil {
		return fmt.Errorf("初始化 NewSchedulePlanConfig 执行计划配置失败 %v", err)
	}
	return
}

// 初始化上下文，该模块的全局配置，过滤表名，按照join、left、uni方式
func schemaContext(config *SchemaInitContextConfig) *CheckSchemaMetaData {
	var event = "[schemaContext]"
	lock.Lock()
	defer lock.Unlock()
	meta := GetCheckSchemaMetaData()
	meta.ObjectSum = len(config.GtCheckConfig.SecondaryL.TableObject)
	if initSchemaBean(config) != nil {
		close(meta.TableObjectFlow)
		return nil
	}
	fmt.Println(fmt.Sprintf("-- [%v] %v report: begin init table object group -- ", time.Now().Format("2006-01-02 15:04:05"), arg.ToolName))
	infoWriteInfo(fmt.Sprintf("%v Start checking whether the group table object exists", event))
	go func() {
		defer func() {
			close(meta.TableObjectFlow)
		}()
		if pubM.ObjectExist(pubM.ObjectFlow{
			Drive:     []string{GlobalPConfigs.dSns.SrcDBName, GlobalPConfigs.dSns.DestDBName},
			DriveJoin: "join",
			Source:    GlobalPConfigs.db.Source,
			Target:    GlobalPConfigs.db.Target,
			Mtc:       GlobalPConfigs.rules.Mtc,
			IgnoreSum: &meta.IgnoreObjectSum,
			Object:    config.GtCheckConfig.SecondaryL.TableObject,
			Flow:      meta.TableObjectFlow,
		}) != nil {
			log.MainLog().Error(fmt.Sprintf("%v Failed to initialize table object flow", event))
		}
	}()
	return meta
}
